Understanding Application State in Flutter
Application state refers to the data that an application needs to keep track of while the application is running and that may need to be shared across multiple widgets, screens, or features.
In Flutter, a useful way to think about state is as the data required to rebuild the UI correctly at any moment. Application state is generally different from temporary widget-specific state because it may need to be accessed by multiple parts of the application or maintained as the user moves through the app.
1. What is Application State?
Application state is information that represents the current condition of an application and can affect what the user sees or what the application does.
For example, an e-commerce application may need to remember:
- Whether the user is logged in.
- Which products are in the shopping cart.
- The selected delivery address.
- User preferences.
- Whether notifications have been read.
- Product data loaded from an API.
- Current application settings.
Flutter describes application state as information such as user preferences, login information, notifications, shopping-cart contents, and read/unread article status. :contentReference[oaicite:0]{index=0}
2. Simple Definition
You can remember application state with this simple definition:
Application State
=
Data that represents the current condition of the application
Another useful concept is:
UI = f(Application State)
This means that the UI is constructed according to the current state of the application. Flutter follows a declarative UI model in which changing state causes the UI to be rebuilt to represent that state. :contentReference[oaicite:1]{index=1}
3. Examples of Application State
| Application State |
Example |
| Authentication State |
User is logged in or logged out |
| User Profile |
Name, email, profile information |
| Shopping Cart |
Products selected by the user |
| Theme |
Light mode or dark mode |
| Notifications |
Read and unread notifications |
| API Data |
Products, users, articles, orders |
| Application Settings |
Language, preferences, configuration |
| Network State |
Online, offline, loading, error |
4. Application State vs Widget State
One of the most important concepts is understanding the difference between application state and ephemeral state.
| Application State |
Ephemeral State |
| Usually shared across multiple parts of an application. |
Usually contained within one widget. |
| May need to survive navigation or application sessions. |
Usually temporary. |
| Examples include login information and shopping cart data. |
Examples include selected tab or animation progress. |
| May require a dedicated state-management approach. |
Often managed with StatefulWidget and setState(). |
Flutter describes ephemeral state as state that can be neatly contained inside a single widget, while application state is shared more broadly and may need to be retained beyond the lifecycle of an individual widget. :contentReference[oaicite:2]{index=2}
5. Example of Ephemeral State
Suppose a screen has a bottom navigation bar. The currently selected tab might only matter to that screen.
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
int selectedIndex = 0;
@override
Widget build(BuildContext context) {
return BottomNavigationBar(
currentIndex: selectedIndex,
onTap: (index) {
setState(() {
selectedIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
);
}
}
Here, selectedIndex is local to the widget, so using setState() is a natural solution. Flutter's documentation uses the selected tab of a bottom navigation bar as a typical example of ephemeral state. :contentReference[oaicite:3]{index=3}
6. Example of Application State
Consider a shopping application. The cart can be accessed by:
- Product screen
- Cart screen
- Checkout screen
- Order summary screen
Because multiple parts of the application need the cart, the cart becomes a good example of shared application state.
Product Screen
|
↓
Cart State
/ \
↓ ↓
Cart Checkout
Screen Screen
7. Why is Application State Important?
Application state is important because modern applications contain information that needs to be shared and updated across multiple screens.
For example, after a user logs in:
Login Screen
↓
Authentication
↓
Logged-in User
↓
Home Screen
↓
Profile Screen
↓
Orders Screen
All of these screens may need information about the currently authenticated user.
8. Application State and Declarative UI
Flutter uses a declarative programming model. Instead of directly telling a widget to change itself, you update the state and allow Flutter to rebuild the UI based on the new state. :contentReference[oaicite:4]{index=4}
For example:
bool isLoggedIn = false;
The UI can be described as:
if (isLoggedIn) {
return const HomeScreen();
} else {
return const LoginScreen();
}
When the state changes:
isLoggedIn = true;
The UI should represent the new state:
Login Screen
↓
Authentication Successful
↓
isLoggedIn = true
↓
Home Screen
9. Common Types of Application State
9.1 Authentication State
Authentication state represents whether a user is currently authenticated.
bool isLoggedIn = false;
Possible states include:
- Not authenticated
- Authenticating
- Authenticated
- Authentication failed
9.2 User State
User state contains information about the current user.
class UserState {
final String id;
final String name;
final String email;
UserState({
required this.id,
required this.name,
required this.email,
});
}
9.3 Shopping Cart State
class CartState {
final List products;
CartState({
required this.products,
});
}
9.4 Theme State
bool isDarkMode = false;
9.5 Network State
enum NetworkStatus {
idle,
loading,
success,
error,
}
9.6 API Data State
API state can contain:
- Loading information
- Retrieved data
- Error information
- Empty-data information
10. Application State Lifecycle
Application state commonly follows a cycle:
Initial State
↓
User Action / System Event
↓
State Changes
↓
Notify UI
↓
UI Rebuilds
↓
New State Displayed
For example:
Cart is Empty
↓
User Adds Product
↓
Cart State Changes
↓
UI Receives New State
↓
Cart Badge Shows "1"
↓
Cart Screen Shows Product
11. Source of Truth
A good application should have a clear source of truth for important application data.
A source of truth means the place responsible for maintaining the authoritative version of a particular piece of state.
For example:
Cart State
↓
Single Source of Truth
↓
Product Screen
Cart Screen
Checkout Screen
Having multiple independent copies of the same important data can lead to synchronization problems.
12. Sharing Application State
When several widgets need the same state, the state can be moved higher in the widget tree or exposed through a suitable state-management mechanism.
Flutter's documentation describes this idea as lifting state up: state is kept above the widgets that need to use it. :contentReference[oaicite:5]{index=5}
Parent
|
+---- Child A
|
+---- Child B
|
+---- Child C
Shared State
↑
Managed by Parent
13. Lifting State Up
Suppose two widgets need access to the same cart.
Instead of maintaining two different cart objects, keep the cart state in a common ancestor or shared state layer.
MyApp
|
├── ProductList
|
└── Cart
The shared cart state can live above both widgets.
This avoids trying to imperatively modify a widget from another unrelated widget. Flutter's declarative model instead encourages constructing widgets from the current state. :contentReference[oaicite:6]{index=6}
14. Application State with ChangeNotifier
ChangeNotifier is a Flutter SDK class that can notify listeners when application data changes.
class CartModel extends ChangeNotifier {
final List _items = [];
List get items => List.unmodifiable(_items);
void addItem(String item) {
_items.add(item);
notifyListeners();
}
void removeItem(String item) {
_items.remove(item);
notifyListeners();
}
int get itemCount => _items.length;
}
Calling notifyListeners() signals listening widgets that the state changed and they should update. :contentReference[oaicite:7]{index=7}
15. Application State with Provider
Provider is a community package that can be used to expose and consume shared application state. Flutter's documentation demonstrates Provider together with ChangeNotifier, ChangeNotifierProvider, and Consumer. :contentReference[oaicite:8]{index=8}
Install Provider:
flutter pub add provider
Example state model:
class CartModel extends ChangeNotifier {
final List _items = [];
List get items => List.unmodifiable(_items);
void addItem(String item) {
_items.add(item);
notifyListeners();
}
void removeItem(String item) {
_items.remove(item);
notifyListeners();
}
}
16. Providing Application State
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CartModel(),
child: const MyApp(),
),
);
}
The provider makes the state object available to widgets below it in the widget tree.
17. Reading Application State
final cart = context.watch();
Text(
'Items: ${cart.items.length}',
)
A widget can also access the model to trigger an action:
context.read().addItem(
'Flutter Course',
);
The exact API usage depends on the Provider package version and application architecture.
18. Application State and Firebase
Application state becomes especially important when working with Firebase.
For example, a Flutter application using Firebase Authentication can maintain:
Authentication State
|
├── Logged Out
|
├── Loading
|
└── Logged In
Cloud Firestore can provide another source of application data:
Firestore
↓
Repository / Service
↓
Application State
↓
Flutter UI
This architecture allows the UI to react to changes in authentication and cloud data.
19. Authentication State Example
StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return const HomeScreen();
}
return const LoginScreen();
},
)
The current authentication state determines which screen is displayed.
20. Application State with API Data
Suppose an application loads a product list from an API.
The application state might look like:
class ProductState {
final bool isLoading;
final List products;
final String? error;
ProductState({
required this.isLoading,
required this.products,
this.error,
});
}
Possible state transitions:
Initial
↓
Loading
↓
Success
↓
Products Displayed
Or:
Initial
↓
Loading
↓
Error
↓
Retry
21. Loading State
When data is being retrieved, the application can maintain a loading state.
bool isLoading = true;
The UI can display:
if (isLoading) {
return const CircularProgressIndicator();
}
22. Success State
After the operation succeeds:
isLoading = false;
products = fetchedProducts;
error = null;
The UI can then display the data.
23. Error State
If the operation fails:
isLoading = false;
error = 'Unable to load products';
The UI can show an error message:
if (error != null) {
return Text(error!);
}
24. Empty State
A successful request may still return no data.
if (products.isEmpty) {
return const Center(
child: Text('No products available'),
);
}
Therefore, a complete application should distinguish between:
- Loading
- Success with data
- Success with no data
- Error
25. Application State and MVVM
In an MVVM-style Flutter architecture, a ViewModel can hold presentation state and notify the UI when that state changes.
View
↓
ViewModel
↓
Repository
↓
Service
↓
API / Firebase / Database
Flutter's architecture guidance describes ViewModels as consumers of repository data, while repositories act as a source of application data.
26. ViewModel Example
class ProductViewModel extends ChangeNotifier {
bool isLoading = false;
List products = [];
String? error;
Future loadProducts() async {
isLoading = true;
error = null;
notifyListeners();
try {
await Future.delayed(
const Duration(seconds: 2),
);
products = [
'Laptop',
'Phone',
'Tablet',
];
} catch (e) {
error = 'Failed to load products';
} finally {
isLoading = false;
notifyListeners();
}
}
}
This demonstrates a ViewModel holding loading, data, and error state and notifying listeners when those values change.
27. Repository and Application State
A repository can act as the source of application data between the data sources and ViewModels.
API / Firebase
↓
Repository
↓
ViewModel
↓
UI
For example:
class UserRepository {
Future getUser() async {
// Fetch data from API or Firebase
throw UnimplementedError();
}
}
The ViewModel can then consume this repository instead of communicating directly with the API or Firebase service.
28. Application-Wide Session State
Some state needs to be shared across multiple features during an application session.
Examples include:
- Current authenticated user
- In-memory cache
- Temporary application configuration
- Current session information
Repositories can act as a source of application data and can also manage app-wide lifecycle state that needs to be shared across multiple ViewModels but does not need to persist beyond the current application session.
29. Application State vs Persistent Data
Application state and persistent data are related but are not exactly the same thing.
| Application State |
Persistent Data |
| Represents current application conditions. |
Stored for later use. |
| May exist only during the current session. |
Designed to survive application restarts. |
| Can be held in memory. |
Can be stored in files, databases, or key-value storage. |
| Example: current loading status. |
Example: saved dark-mode preference. |
State restoration and persistent storage solve different problems. Flutter provides restoration mechanisms for supported short-term UI state, while longer-term application data generally requires persistent storage. :contentReference[oaicite:9]{index=9}
30. State Restoration
State restoration is the process of restoring certain UI state after an application is recreated.
Examples of short-term state can include:
- Selected tab
- Scroll position
- Unsubmitted form values
- Navigation position
Flutter provides a restoration framework for supported widgets and application components. :contentReference[oaicite:10]{index=10}
31. Application State Flow
A typical application can follow this flow:
User Action
↓
Event Handler
↓
State Update
↓
State Notification
↓
Widget Rebuild
↓
Updated UI
For data loaded from an external source:
User Action
↓
ViewModel
↓
Repository
↓
API / Firebase
↓
New Data
↓
ViewModel State
↓
UI Rebuild
32. Practical Example: Login Application State
Consider a login application with three main states:
enum AuthStatus {
loggedOut,
loading,
loggedIn,
}
The state can be used to control the UI:
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.loading:
return const LoadingScreen();
case AuthStatus.loggedIn:
return const HomeScreen();
case AuthStatus.loggedOut:
return const LoginScreen();
}
}
This approach makes different application states explicit and easier to reason about.
33. Practical Example: Shopping Cart
class CartState extends ChangeNotifier {
final List _items = [];
List get items => List.unmodifiable(_items);
int get itemCount => _items.length;
void addProduct(String product) {
_items.add(product);
notifyListeners();
}
void removeProduct(String product) {
_items.remove(product);
notifyListeners();
}
void clearCart() {
_items.clear();
notifyListeners();
}
}
Multiple screens can observe this state:
Product Screen
↓
addProduct()
↓
Cart State
↓
notifyListeners()
↓
Cart Screen updates
↓
Checkout Screen updates
34. Practical Example: Theme State
class ThemeState extends ChangeNotifier {
bool isDark = false;
void toggleTheme() {
isDark = !isDark;
notifyListeners();
}
}
The application theme can then depend on this state:
MaterialApp(
themeMode: isDark
? ThemeMode.dark
: ThemeMode.light,
)
35. Practical Example: Notification State
class NotificationState extends ChangeNotifier {
int unreadCount = 0;
void markAsRead() {
if (unreadCount > 0) {
unreadCount--;
notifyListeners();
}
}
void addNotification() {
unreadCount++;
notifyListeners();
}
}
The notification badge can display the current state:
Badge(
label: Text('$unreadCount'),
child: const Icon(
Icons.notifications,
),
)
36. State Management Options
Flutter provides several mechanisms for managing state. Built-in approaches include:
setState()
ValueNotifier
InheritedNotifier
InheritedWidget
InheritedModel
There are also many community packages for state management. Flutter's documentation notes that the appropriate choice depends on the application's complexity, team preferences, and the problem being solved. :contentReference[oaicite:11]{index=11}
37. When to Use Local State
Use local state when:
- Only one widget needs the data.
- The state is temporary.
- The state does not need to be shared.
- The state does not need complex business logic.
Example:
bool isPasswordVisible = false;
38. When to Use Application State
Application state becomes appropriate when:
- Multiple screens need the same data.
- Multiple widgets need to react to the same changes.
- The state represents important application information.
- The state needs a central source of truth.
- The state is connected to business logic.
- The state comes from APIs, databases, or Firebase.
There is no universal rule for classifying every variable. Flutter's documentation explicitly notes that state classification depends on the application and can change as the application grows. :contentReference[oaicite:12]{index=12}
39. Common Mistakes in Application State Management
- Keeping duplicate copies of the same state.
- Passing state through too many widget levels unnecessarily.
- Putting all application state into one enormous class.
- Mixing UI code with API or Firebase code.
- Not handling loading and error states.
- Not defining a clear source of truth.
- Using complex state-management solutions for very small local state.
- Forgetting to notify listeners after changing a
ChangeNotifier.
- Keeping sensitive information directly in UI widgets.
- Not considering whether data needs to survive an application restart.
40. Best Practices
- Keep state as close as practical to the widgets that use it.
- Lift state up when multiple widgets need the same state.
- Use a clear source of truth for shared application data.
- Separate UI, state-management, repository, and service responsibilities.
- Use
setState() for simple local state.
- Use an appropriate shared-state approach for larger application state.
- Represent loading, success, empty, and error states explicitly.
- Avoid unnecessary widget rebuilds.
- Keep API and Firebase operations outside presentation widgets when the application architecture requires separation.
- Use persistent storage when information must survive application restarts.
- Choose a state-management approach based on application requirements rather than complexity for its own sake.
41. Practical Application Architecture
Flutter UI
↓
View / Screen
↓
ViewModel / State Manager
↓
Repository
↓
Service
↓
Firebase / REST API / Database
This type of layered architecture can help separate presentation logic from data access and application logic. Flutter's architecture guidance describes relationships between views, view models, repositories, and services.
42. Example Project Structure
lib/
├── main.dart
├── models/
│ ├── user.dart
│ └── product.dart
├── views/
│ ├── login_screen.dart
│ ├── home_screen.dart
│ ├── cart_screen.dart
│ └── profile_screen.dart
├── viewmodels/
│ ├── auth_viewmodel.dart
│ ├── product_viewmodel.dart
│ └── cart_viewmodel.dart
├── repositories/
│ ├── auth_repository.dart
│ └── product_repository.dart
└── services/
├── firebase_service.dart
└── api_service.dart
43. Complete Application State Example
Consider a shopping application:
Application
│
├── Authentication State
│ ├── Logged Out
│ ├── Loading
│ └── Logged In
│
├── User State
│ ├── Name
│ ├── Email
│ └── Profile
│
├── Product State
│ ├── Loading
│ ├── Products
│ ├── Empty
│ └── Error
│
├── Cart State
│ ├── Items
│ ├── Quantity
│ └── Total
│
└── Theme State
├── Light
└── Dark
44. Application State with Firebase Architecture
A Firebase-powered Flutter application can use the following architecture:
Flutter UI
↓
State Manager / ViewModel
↓
Repository
↓
Firebase Service
↓
Firebase Authentication
Cloud Firestore
Firebase Storage
Firebase Messaging
For example, a login operation could follow:
Login Button
↓
Auth ViewModel
↓
Auth Repository
↓
Firebase Authentication
↓
User Credential
↓
Authentication State
↓
Home Screen
45. Application State and Persistence
Some application state needs to remain available after the application is closed and reopened.
Examples:
- Dark mode preference
- Selected language
- Onboarding completion
- Saved user preferences
- Offline application data
Such information requires a persistence mechanism rather than relying only on in-memory state. Flutter's architecture documentation includes key-value storage as one option for storing simple application data such as configuration and preferences.
46. In-Memory State vs Persistent State
| In-Memory State |
Persistent State |
| Stored while the application is running. |
Stored for use after restart. |
| Fast to access. |
Requires storage read/write operations. |
| Can disappear when the application process ends. |
Designed to survive application restarts. |
| Example: loading status. |
Example: saved theme preference. |
47. Important Difference: State vs Data
Not all data in an application has to be treated as application state.
For example:
Product catalog stored on server
↓
Repository retrieves data
↓
ViewModel exposes required state
↓
UI displays products
The server or database is the persistent source of the product data, while the application may maintain a current in-memory representation of that data as part of its UI state.
48. No Universal Rule
It is important to understand that there is no universal rule saying that a particular variable must always be application state or always be local state.
For example, a selected tab might initially be local to one screen. Later, the product requirements may require another part of the application to change the selected tab or restore it between sessions. In that situation, the same concept may become application state.
Flutter's documentation explicitly notes that the distinction between ephemeral and application state is conceptual rather than a strict technical rule. :contentReference[oaicite:13]{index=13}
49. Interview Questions
Q1. What is application state?
Application state is data representing the current condition of an application that may need to be shared across multiple parts of the application.
Q2. What are examples of application state?
Examples include authentication information, user preferences, shopping cart contents, notifications, and shared API data.
Q3. What is the difference between application state and ephemeral state?
Ephemeral state is generally local to a single widget, while application state is shared more broadly and may need to survive longer than the lifecycle of an individual widget.
Q4. What is lifting state up?
Lifting state up means keeping shared state above the widgets that need to use it so those widgets can work from the same source of truth.
Q5. What is a source of truth?
A source of truth is the authoritative place responsible for maintaining a particular piece of application data.
Q6. Why is application state important?
It allows different parts of an application to share and react to common data without maintaining disconnected copies of the same information.
Q7. What is ChangeNotifier?
ChangeNotifier is a Flutter SDK class that can notify listeners when its data changes.
Q8. What does notifyListeners() do?
It signals listening widgets that the state has changed so they can rebuild and display the updated state.
Q9. Does every variable need a state-management package?
No. Simple widget-specific state can often be managed with StatefulWidget and setState(). More complex or shared state may benefit from another state-management approach.
Q10. What is persistent state?
Persistent state is information stored so that it can be recovered after an application restart, such as user preferences.
50. Quick Revision
Application State
↓
Shared or important application data
Examples
↓
Login
Cart
User Profile
Theme
Notifications
API Data
Local State
↓
Usually belongs to one widget
Application State
↓
Usually shared across features
Lifting State Up
↓
Move shared state above widgets that use it
Source of Truth
↓
Authoritative location for important data
Persistent State
↓
Data saved for future sessions
51. Learning Outcome
After completing this topic, you should be able to explain what application state means, identify common types of application state, distinguish application state from ephemeral state, understand why shared state needs a clear source of truth, explain lifting state up, understand how ChangeNotifier and Provider can be used for shared state, and understand how application state can connect UI, ViewModels, repositories, Firebase, APIs, and persistent storage.
52. Useful Flutter Resources
53. JustAcademy Flutter Resources
For structured Flutter training and practical application development, explore the following resources:
54. Summary
Application state is the data that represents the current condition of an application and may need to be shared by multiple screens or features. Examples include login information, user preferences, shopping cart contents, notifications, and shared data loaded from APIs or Firebase.
Simple local state can often be handled with StatefulWidget and setState(), while shared application state can be managed using approaches such as lifting state up, ChangeNotifier, Provider, or other suitable state-management solutions. Flutter does not impose one universal state-management architecture; the appropriate choice depends on the application's requirements and complexity. :contentReference[oaicite:14]{index=14}
The most important concept to remember is:
Application State
↓
Single Source of Truth
↓
State Changes
↓
UI Rebuilds
↓
User Sees Updated Application